/* Gauge - Performance Measurement Framework for Mobile Platforms
 * Copyright (C) 2013, Juha Hytonen <juha.a.hytonen@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.gauge.GaugeAgent;

import java.util.ArrayList;
import java.util.List;

import org.gauge.Workload;

import org.gauge.GaugeAgent.BackgroundWorkloadRunner;
import org.gauge.GaugeAgent.TimedWorkloadRunner;
import org.gauge.GaugeAgent.PeriodicWorkloadRunner;

import android.content.Context;

/**
 * Auxiliary class for BenchmarkController. Handlers workloads and workload runners.
 * 
 * @see BenchmarkController
 * @see Workload
 * @see WorkloadController
 */
public class WorkloadController {
	
	private final static int MaximumMillisecsToWaitForBackgroundWorkload = 600000; // 10 minutes
	private final static int BackgroundWorkloadWaitInterval = 1000; // 1 second
	
	private Context context;
	private List<BackgroundWorkloadRunner> backgroundWorkloads;
	private List<TimedWorkloadRunner> timedWorkloads;
	private List<PeriodicWorkloadRunner> periodicWorkloads;
    
    public WorkloadController() {
    	backgroundWorkloads = new ArrayList<BackgroundWorkloadRunner>();
    	timedWorkloads = new ArrayList<TimedWorkloadRunner>();
    	periodicWorkloads = new ArrayList<PeriodicWorkloadRunner>();
    }
    
	public void SetContext(Context context) {
		this.context = context;
	}
    
	public void StopAllWorkloads() throws Exception {
		stopBackgroundWorkloads();
		stopTimedWorkloads();
		stopPeriodicWorkloads();
	}
	
	private void stopBackgroundWorkloads() throws Exception {
		for(BackgroundWorkloadRunner runner : backgroundWorkloads) {
			runner.Stop();
		}
		backgroundWorkloads.clear();
	}
	
	private void stopTimedWorkloads() throws Exception {
		for(TimedWorkloadRunner runner : timedWorkloads) {
			runner.Stop();
		}
		timedWorkloads.clear();
	}
	
	private void stopPeriodicWorkloads() throws Exception {
		for(PeriodicWorkloadRunner runner : periodicWorkloads) {
			runner.Stop();
		}
		periodicWorkloads.clear();
	}
	
	public void RunWorkload(Class<? extends Workload> workloadClass) throws Exception {
		try {
			Workload workload = createWorkload(workloadClass);
			BackgroundWorkloadRunner runner = createBackgroundWorkloadRunner(workload);
			runner.Start();
		}
		catch(Exception e) {
			throw e;
		}
	}
	
	private Workload createWorkload(Class<? extends Workload> workloadClass) throws Exception {
		try {
			Workload workload = workloadClass.newInstance();
			workload.SetContext(context);
			return workload;
		}
		catch(Exception e) { // IllegalAccessException, InstantiationException
			throw new Exception("Failed to create workload");
		}
	}
	
	private BackgroundWorkloadRunner createBackgroundWorkloadRunner(Workload workload) {
		BackgroundWorkloadRunner runner = new BackgroundWorkloadRunner(workload);
		backgroundWorkloads.add(runner);
		return runner;
	}

	public void RunWorkloadFor(Class<? extends Workload> workloadClass, int millisecs) throws Exception {
		try {
			Workload workload = createWorkload(workloadClass);
			TimedWorkloadRunner runner = createTimedWorkloadRunner(workload, millisecs);
			runner.Start();
		}
		catch(Exception e) {
			throw e;
		}		
	}
	
	private TimedWorkloadRunner createTimedWorkloadRunner(Workload workload, int millisecsToRun) {
		TimedWorkloadRunner runner = new TimedWorkloadRunner(workload, millisecsToRun);
		timedWorkloads.add(runner);
		return runner;
	}

	public void RunWorkloadEvery(Class<? extends Workload> workloadClass, int millisecs) throws Exception {
		try {
			Workload workload = createWorkload(workloadClass);
			PeriodicWorkloadRunner runner = createPeriodicWorkloadRunner(workload, millisecs);
			runner.Start();
		}
		catch(Exception e) {
			throw e;
		}
	}

	private PeriodicWorkloadRunner createPeriodicWorkloadRunner(Workload workload, int millsecsBetweenRuns) {
		PeriodicWorkloadRunner runner = new PeriodicWorkloadRunner(workload, millsecsBetweenRuns);
		periodicWorkloads.add(runner);
		return runner;
	}
	
	public void WaitForWorkloadToStop(Class<? extends Workload> workloadClass) throws Exception {
		try {
			BackgroundWorkloadRunner runner = getRunnerForWorkload(workloadClass);
			int millisecsWaited = 0;
			while(runner.IsRunning() && (millisecsWaited < MaximumMillisecsToWaitForBackgroundWorkload)) {
				org.gauge.Util.WaitFor(BackgroundWorkloadWaitInterval);
				millisecsWaited += BackgroundWorkloadWaitInterval;
			}
			
			if (runner.IsRunning()) {
				throw new Exception("Could not stop workload");
			}
		}
		catch(Exception e) {
			throw e;
		}
    }

	public void StopRunningWorkload(Class<? extends Workload> workloadClass) throws Exception {
		try {
			BackgroundWorkloadRunner runner = getRunnerForWorkload(workloadClass);
			runner.Stop();
		}
		catch(Exception e) {
			throw e;
		}
    }
	
	private BackgroundWorkloadRunner getRunnerForWorkload(Class<? extends Workload> workloadClass) throws Exception {
		String workloadClassName = workloadClass.getName();
		for(BackgroundWorkloadRunner workloadRunner : backgroundWorkloads) {
			String name = workloadRunner.GetWorkloadClassName();
			if (name.equals(workloadClassName)) {
				return workloadRunner;
			}
		}
		throw new Exception("No such workload");
	}
	
}
